from django.core.management.base import BaseCommand
from django.utils import timezone
import paho.mqtt.client as mqtt
from django.conf import settings
from device.models import Device
from django.db.models import Q
import ujson
import time
import threading
from collections import defaultdict
from django.core.cache import cache

# MQTT 配置（建议在 settings.py 中定义）
MQTT_SETTINGS = {
# 1.95.196.53 服务器
#     "broker": getattr(settings, "MQTT_BROKER", "127.0.0.1"),  # 服务器地址
    "broker": getattr(settings, "MQTT_BROKER", "1.95.196.53"),  # 服务器地址
    "port": getattr(settings, "MQTT_PORT", 1883),            # 端口
    "topic": getattr(settings, "MQTT_TOPIC", "#"),           # 订阅主题（# 表示所有主题）
    "username": getattr(settings, "MQTT_USERNAME", ""),      # 用户名（无则留空）
    "password": getattr(settings, "MQTT_PASSWORD", "")       # 密码（无则留空）
}
timeout = 60
client = None
# 订阅列表
# /esp32/topic/status 设备状态
MQTT_TOPIC = ['/esp32/topic/status']
# 存储设备最后心跳时间：{设备ID: 时间戳}
device_heartbeats = defaultdict(float)
class Command(BaseCommand):
    help = "连接 MQTT 服务并将消息存储到数据库"

    def handle(self, *args, **options):
        # 初始化 MQTT 客户端
        client = mqtt.Client()

        # 设置认证（若有用户名密码）
        if MQTT_SETTINGS["username"] and MQTT_SETTINGS["password"]:
            client.username_pw_set(
                MQTT_SETTINGS["username"],
                MQTT_SETTINGS["password"]
            )

        # 连接回调函数：连接成功后订阅主题
        def on_connect(client, userdata, flags, rc):
            if rc == 0:
                # client.publish(MQTT_TOPIC[0], b"", qos=0)
                # set_mqtt_status(True)
                print(f"已连接到 MQTT 服务器: {MQTT_SETTINGS['broker']}")
                for item in MQTT_TOPIC:
                 #client.subscribe(MQTT_SETTINGS["topic"])  # 订阅主题
                    client.subscribe(item)  # 订阅主题
            else:
                print(f"连接失败，错误码: {rc}")

        # 消息回调函数：接收消息并存储到数据库
        def on_message(client, userdata, msg):
            try:
                # 解析消息
                topic = msg.topic
                payload = msg.payload.decode("utf-8")  # 消息内容转字符串
                if topic == MQTT_TOPIC[0]:
                    data_dict = ujson.loads(payload)  # 核心步骤：转回字典
                    device_id=data_dict.get('device_id')
                    status=data_dict.get('status')
                    print(f"转回字典: {data_dict}")
                    # 更新最后心跳时间
                    device_heartbeats[device_id] = time.time()
                    Device.objects.filter(id=device_id).update(status=status,updated_at=timezone.now())
                    print('成功')
                # 存储到数据库
                print(f"存储成功 → 主题: {topic}, 内容: {payload}")

            except Exception as e:
                print(f"存储失败: {str(e)}")

        # 绑定回调函数
        client.on_connect = on_connect
        client.on_message = on_message

        # 连接 MQTT 服务器并持续监听
        try:
            client.connect(MQTT_SETTINGS["broker"], MQTT_SETTINGS["port"], keepalive=60)
            print("开始监听 MQTT 消息...（按 Ctrl+C 停止）")
            client.loop_forever()  # 阻塞式监听，确保持续运行
        except Exception as e:
            print(f"无法连接到 MQTT 服务器: {str(e)}")
            # self.stdout.write(self.style.ERROR(f"无法连接到 MQTT 服务器: {str(e)}"))

# 定时检查超时设备
offline_candidates = {}  # 临时记录首次判定离线的设备


# 初始化设备状态
def init_device():
    online_devices = Device.objects.filter(Q(status__icontains='online'))
    # 2. 批量修改状态值为 'offline'（返回更新的行数）
    updated_count = online_devices.update(status='offline',updated_at=timezone.now())
    print(f"成功更新 {updated_count} 个离线设备")

def check_offline():
    while True:
        current_time = time.time()
        # 1. 标记超时设备为候选
        for device_id, last_beat in device_heartbeats.items():
            if current_time - last_beat > 30 and device_id not in offline_candidates:
                offline_candidates[device_id] = current_time  # 记录首次超时时间

        # 2. 候选设备超过10秒仍离线，确认删除
        for device_id, first_timeout in list(offline_candidates.items()):
            if current_time - first_timeout > timeout:
                del device_heartbeats[device_id]
                del offline_candidates[device_id]
                Device.objects.filter(id=device_id).update(status='offline',condition=False,updated_at=timezone.now())
                print(f"设备 {device_id} 确认离线，删除记录")
        time.sleep(5)
# 初始化设备状态
init_device()
threading.Thread(target=check_offline, daemon=True).start()
